package io.kiki.stack.netty.protocol;

import io.kiki.stack.netty.command.CommandCode;
import io.kiki.stack.netty.config.ConfigManager;
import io.kiki.stack.netty.processor.Processor;
import io.kiki.stack.netty.util.NamedThreadFactory;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * Manager of processors<br>
 * Maintains the relationship between command and command processor through command code.
 */

@Slf4j
@Setter
@Getter
public class ProcessorManager {

    private final ConcurrentHashMap<CommandCode, Processor<?>> map = new ConcurrentHashMap<CommandCode, Processor<?>>(4);
    private final int minPoolSize = ConfigManager.default_tp_min_size();
    private final int maxPoolSize = ConfigManager.default_tp_max_size();
    private final int queueSize = ConfigManager.default_tp_queue_size();
    private final long keepAliveTime = ConfigManager.default_tp_keepalive_time();
    /**
     * The default executor, if no executor is set for processor, this one will be used
     */
    private Executor defaultExecutor;
    private Processor<?> defaultProcessor;


    public ProcessorManager() {
        defaultExecutor = new ThreadPoolExecutor(minPoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize), new NamedThreadFactory("-default-executor", true));
    }


    public void registerProcessor(CommandCode commandCode, Processor<?> processor) {
        if (this.map.containsKey(commandCode)) {
            log.warn("Processor for cmd={} is already registered, the processor is {}, and changed to {}", commandCode, map.get(commandCode).getClass().getName(), processor.getClass().getName());
        }
        this.map.put(commandCode, processor);
    }


    public void registerDefaultProcessor(Processor<?> processor) {
        if (this.defaultProcessor == null) {
            this.defaultProcessor = processor;
        } else {
            throw new IllegalStateException("The defaultProcessor has already been registered: " + this.defaultProcessor.getClass());
        }
    }


    public Processor<?> getProcessor(CommandCode commandCode) {
        Processor<?> processor = this.map.get(commandCode);
        if (processor != null) {
            return processor;
        }
        return this.defaultProcessor;
    }


}